package dm.ast;

import test.envset.GlobalState;
import test.utility.Randomly;

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

public class DmFrom extends DmASTNode {
    public List<DmTableSource> fromTables = null; // 查询源表, 可能包含子查询
    public int on_wid = 0;//on的总宽度
    public List<DmJoinExpression> joinlist = null;// join类型

    public void addTable(DmTableSource tabSource) {
        if (fromTables == null) {
            fromTables = new ArrayList<DmTableSource>();
        }
        fromTables.add(tabSource);
    }

    public int getTablesCnt() {
        if (fromTables == null) {
            return 0;
        } else {
            return fromTables.size();
        }
    }

    @Override
    public void setWidt() {
        if (fromTables == null) {
            wid_t = 0;
        } else {
            wid_t = 1; // From关键字
            for (int i = 0; i < fromTables.size(); i++) {
                fromTables.get(i).setWidt();
                wid_t += fromTables.get(i).wid_t;
            }

            if (joinlist != null) {
                wid_t += on_wid;
            }
        }
    }

    @Override
    public void setDept() {
        if (fromTables == null) {
            dep_t = 0;
        } else {
            dep_t = 0;
            for (int i = 0; i < fromTables.size(); i++) {
                fromTables.get(i).setDept();
                dep_t += fromTables.get(i).dep_t;
            }
            if (joinlist != null) {
                for (int i = 0; i < joinlist.size(); i++) {
                    if (joinlist.get(i) != null) {
                        joinlist.get(i).setDept();
                    }
                }
            }
        }
    }


    @Override
    public DmDataType getExpressionType() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public String toSql() {
        if (fromTables == null) {
            return " " + this.getClass().getSimpleName();
        } else {
            StringBuilder sql = new StringBuilder();
            sql.append(" FROM " + fromTables.get(0).toSql());
            if (joinlist == null) {//不使用join连接方式
                for (int i = 1; i < fromTables.size(); i++) {
                    sql.append(" , " + fromTables.get(i).toSql());
                }
            } else {//使用join连接方式
                for (int i = 1; i < fromTables.size(); i++) {
                    if (joinlist.size() > i - 1) {
                        DmJoinExpression on = joinlist.get(i - 1);
                        if (on.joinbool != null) {
                            //crossjoin或,
                            sql.append(joinTosql(on) + fromTables.get(i).toSql() + on.toSql());
                        } else {
                            if (on.using != null) {
                                //join using
                                String using = String.format(" USING(%s) ", on.using.name);
                                sql.append(joinTosql(on) + fromTables.get(i).toSql() + using.toString());
                            } else {
                                //join、innerjoin、outerjoin、naturaljoin
                                sql.append(joinTosql(on) + fromTables.get(i).toSql());
                            }
                        }
                    }
                }
            }
            return sql.toString();
        }
    }

    @Override
    public void fill(GlobalState globalState) {
        if (!isCompleted) {
            for (int i = 0; i < fromTables.size(); i++) {
                DmTableSource tmp = fromTables.get(i);
                //解决from中子查询的别名设置问题
                //利用节点中的sel_level变量,表明了层次
                if (tmp instanceof DmSubSelect) {
                    tmp.setAlias(globalState.tabAlias());
                }
                tmp.fill(globalState);
                //将该查询表加入查询来源缓存表
                globalState.addSelSource(tmp);
            }

            if (joinlist != null) {
                //左边用一个列表里边存放所有符合要求的表 右边一个单表
                for (int j = 0; j < joinlist.size(); j++) {
                    if (joinlist.get(j).jointype != DmJoinType.CommaJoin
                            && joinlist.get(j).jointype != DmJoinType.CrossJoin) {

                        //自然连接
                        if (joinlist.get(j).jointype == DmJoinType.NaturalJoin) {
                            List<DmTableSource> tables1 = new ArrayList<>();//存放连接词左边的表
                            List<DmColumn> colsColumns = null;//同名列

                            //将连接词左边链接的表放进tables1
                            for (int i = j; i <= j && i >= 0; i--) {
                                //连接词不是逗号将表放入tables1
                                if (joinlist.get(i).jointype != DmJoinType.CommaJoin) {
                                    tables1.add(globalState.getSelSource(i));
                                } else {
                                    break;
                                }
                            }

                            //获取同名列
                            colsColumns = SameColumn(tables1, globalState.getSelSource(j + 1));

                            //先判断是否有同名列 没有同名列就尝试改造
                            if (colsColumns == null) {
                                //构造同名列
                                BuildSameColumn(tables1, globalState.getSelSource(j + 1), fromTables);
                                //获取同名列
                                colsColumns = SameColumn(tables1, globalState.getSelSource(j + 1));
                            }

                            //join using
                            if (joinlist.get(j).using != null && colsColumns != null) {
                                DmColumn column = new DmColumn();

                                do {//选取同名列要满足要求 防止选取的这个同名列在大于两个表中出现
                                    column = Randomly.fromList(colsColumns);
                                    if (IsQualified(fromTables, column.name) && (column.reType == DmDataType.INT || column.reType == DmDataType.CHAR)) {
                                        break;
                                    }
                                    colsColumns.remove(column);
                                    column = null;
                                } while (colsColumns.size() > 0);
                                joinlist.get(j).using = column;

                                //没有符合要求的同名列 这时生成的连接词将是逗号，不用再标记同名列 退出本次循环
                                if (column == null) {
                                    joinlist.get(j).jointype = DmJoinType.CommaJoin;
                                    continue;
                                }
                                //标记同名列
                                tagColumn(tables1, globalState.getSelSource(j + 1), joinlist.get(j).using);
                            } else {// natural join
                                joinlist.get(j).using = null;

                                //判断natural join 两边连接的同名列是否符合要求
                                if (colsColumns != null) {
                                    for (DmColumn col : colsColumns) {
                                        //判断同名列是否在大于两个表中出现
                                        if (!IsQualified(fromTables, col.name)) {
                                            joinlist.get(j).jointype = DmJoinType.CommaJoin;
                                        }
                                    }
                                }

                                //join的类型改变为逗号了，不用再标记同名列 退出本次循环
                                if (joinlist.get(j).jointype == DmJoinType.CommaJoin) {
                                    continue;
                                }
                                //标记同名列
                                tagColumn(tables1, globalState.getSelSource(j + 1), joinlist.get(j).using);
                            }
                        } else {//join on威outer join on 威inner join on
                            joinlist.get(j).tableIndex = j;//用来记录两个表的位置
                            List<DmSingleJoinExpression> joins = joinlist.get(j).joinbool;
                            //如果表中列的数量小于joinbool里边表达式的数量 进行截取
                            if (globalState.getSelSource(j).cols.size() < joinlist.get(j).joinbool.size()) {
                                joinlist.get(j).joinbool = joins.subList(0, globalState.getSelSource(j).cols.size() + 1);
                                joinlist.get(j).ops = joinlist.get(j).ops.subList(0, globalState.getSelSource(j).cols.size());
                            }
                            //填充on后边表达式
                            joinlist.get(j).fill(globalState);
                        }
                    }
                }
            }
        }
    }

    //构造同名列 tabs1表示连接词左边的表 tab2表示连接词右边的表 alltables表示本层所有表
    public void BuildSameColumn(List<DmTableSource> tab1, DmTableSource tab2, List<DmTableSource> alltables) {
        List<DmColumn> columns1 = new ArrayList<>();//连接词左边的列
        List<DmColumn> columns2 = tab2.cols; //连接词右边的列
        DmSubSelect t1 = null;// 连接词左边的子查询
        DmSubSelect t2 = null;// 连接词右边的子查询
        for (int i = 0; i < tab1.size(); i++) {
            if (tab1.get(i) instanceof DmSubSelect) {
                t1 = (DmSubSelect) tab1.get(i);
            }
            columns1.addAll(tab1.get(i).cols);
        }
        if (tab2 instanceof DmSubSelect) {
            t2 = (DmSubSelect) tab2;
        }
        if (t1 != null) {
            columns1 = t1.cols;
        }

        int count = 0;//记录是否构造了同名列
        if (t1 == null && t2 == null) {
            return;
        } else {
            for (int i = 0; i < columns1.size(); i++) {
                String originalname1 = columns1.get(i).getOriginalName();//子查询中列的初始名
                String name1 = columns1.get(i).getName();//子询中的列名

                for (int j = 0; j < columns2.size(); j++) {
                    String originalname2 = columns2.get(j).getOriginalName();
                    String name2 = columns2.get(j).getName();
                    // 子查询 连接 子查询
                    if ((originalname1 != null && originalname2 != null) && originalname1.equals(originalname2)) {
                        columns2.get(j).name = name1;
                        DmSelectElements elements = (DmSelectElements) t2.s.subClause[DmSelect.INDEX_ELEMENT];
                        elements.elements.get(j).setSelEleAlias(name1);
                        count = 1;
                        break;
                    }

                    // 表 连接 子查询
                    if (originalname2 != null && name1.equals(originalname2)) {
                        if (IsQualified(alltables, name1)) {
                            columns2.get(j).name = name1;
                            t2 = (DmSubSelect) tab2;
                            DmSelectElements elements = (DmSelectElements) t2.s.subClause[DmSelect.INDEX_ELEMENT];
                            elements.elements.get(j).setSelEleAlias(name1);
                            count = 1;
                            break;
                        }
                    }

                    //子查询 连接 表
                    if (originalname1 != null && originalname1.equals(name2)) {
                        if (IsQualified(alltables, name2)) {
                            columns1.get(i).name = name2;
                            DmSelectElements elements = (DmSelectElements) t1.s.subClause[DmSelect.INDEX_ELEMENT];
                            elements.elements.get(i).setSelEleAlias(name2);
                            count = 1;
                            break;
                        }
                    }
                }

                //已构造一个同名列
                if (count == 1) {
                    break;
                }
            }
        }
    }

    //返回自然连接同名列 tabs1表示连接词左边的表 tab2表示连接词右边的表
    public List<DmColumn> SameColumn(List<DmTableSource> tab1, DmTableSource tab2) {
        List<DmColumn> columns1 = new ArrayList<>();
        for (int i = 0; i < tab1.size(); i++) {
            columns1.addAll(tab1.get(i).cols);
        }
        List<DmColumn> columns2 = tab2.cols;
        List<DmColumn> sameColumns = null;

        for (DmColumn col1 : columns1) {
            String name1 = col1.getName();
            for (int i = 0;  i< columns2.size(); i++) {
                String name2 = columns2.get(i).getName();
                if (name1.equals(name2) && col1.reType == columns2.get(i).reType) {
                    if (sameColumns == null) {
                        sameColumns = new ArrayList<>();
                    }
                    sameColumns.add(col1);
                    sameColumns.add(columns2.get(i));
                    break;
                }
            }
        }
        return sameColumns;
    }

    //标记同名列
    public void tagColumn(List<DmTableSource> tabs1, DmTableSource tab2,DmColumn column) {
        List<DmColumn> columns1 = new ArrayList<>();
        for (int i = 0; i < tabs1.size(); i++) {
            columns1.addAll(tabs1.get(i).cols);
        }
        List<DmColumn> columns2 = tab2.cols;

        //natural join
        if(column == null) {
            for (DmColumn col1 : columns1) {
                for (DmColumn col2 : columns2) {
                    if (col1.name.equals(col2.name)) {
                        col1.tag = 1;
                        col2.tag = 1;
                        break;
                    }
                }
            }
        }else {//join using(c1)
            for (DmColumn col1 : columns1) {
                if (col1.name.equals(column.name)) {
                    col1.tag = 1;
                }
            }
            for (DmColumn col2 : columns2) {
                if (col2.name.equals(column.name)) {
                    col2.tag = 1;
                }
            }
        }
    }

    //判断选的这个列明是否在多个表中出现 colname同名列的名字
    public Boolean IsQualified(List<DmTableSource> tabs1,String colname) {
        List<DmColumn> columns1 = new ArrayList<>();
        int count = 0;
        for (int i = 0; i < tabs1.size(); i++) {
            columns1.addAll(tabs1.get(i).cols);
        }
        for (DmColumn col1 : columns1) {
            String name = col1.getName();
            String originalname = col1.getOriginalName();
            if (name.equals(colname) || (originalname != null && originalname.equals(colname))) {
                count += 1;
            }
        }
        if (count > 2) {
            return false;
        } else {
            return true;
        }
    }

    public static String joinTosql(DmJoinExpression on) {
        switch (on.jointype) {
            case CrossJoin:
                return " CROSS JOIN ";
            case NaturalJoin:
                if (on.using != null) {
                    return " JOIN ";
                } else {
                    return " NATURAL JOIN ";
                }
            case Join:
                return " JOIN ";
            case InnerJoin:
                return " INNER JOIN ";
            case OuterJoin:
                if (on.rightOrleft == 0) {
                    return " LEFT OUTER JOIN ";
                } else {
                    return " RIGHT OUTER JOIN ";
                }
            case CommaJoin:
                return " , ";
            default:
                return " ";
        }
    }

    // 处理sql错误
    // -4097|单行子查询返回多行
    public void fixErr4097() {
        for (int i = 0; i < fromTables.size(); i++) {
            DmTableSource tmp = fromTables.get(i);
            if (tmp instanceof DmSubSelect) {
                DmSelect s = ((DmSubSelect) tmp).s;
                s.fixErr4097();
            }
        }
    }

}
